package tp.pr2;

import java.util.Scanner;

/**
 * This class represents the robot engine. It controls robot movements by
 * processing the instructions introduced with the keyboard. The engine stops
 * when the robot arrives at the space ship, runs out of fuel or receives a quit
 * instruction.
 * 
 * The robot engine is also responsible for updating the fuel level and the
 * recycled material according to the actions that the robot performs in the
 * city.
 * 
 * The robot engine also contains an inventory where the robot stores the items
 * that it collects from the city
 */
public class RobotEngine {
	private Place currentPlace;
	private Direction currentDirection = Direction.NORTH;
	private City cityMap;
	private ItemContainer items;
	private int recycledMaterial;
	private int fuel;

	private static final String LINE_SEPARATOR = System
			.getProperty("line.separator");

	/**
	 * Creates the robot engine in an initial place, facing an initial direction
	 * and with a city map. Initially the robot has not any items or recycled
	 * material but it has an initial amount of fuel (50)
	 * 
	 * @param cityMap
	 *            - The city where the robot wanders
	 * @param initialPlace
	 *            - - The place where the robot starts
	 * @param dir
	 *            - The initial direction where the robot is facing
	 */
	public RobotEngine(City cityMap, Place initialPlace, Direction dir) {
		this.cityMap = cityMap;
		this.currentPlace = initialPlace;
		this.currentDirection = dir;
		items = new ItemContainer();
		recycledMaterial = 0;
		fuel = 50;
	}

	/**
	 * Se encarga de procesar una instruccion Move. En caso de exito (es posible
	 * moverse), actualiza currentPlace. Ademas, muestra mensajes por pantalla
	 * segun concluya la operacion
	 */
	private void move() {
		boolean success = false;

		// Rastrea el mapa de la ciudad hasta que encuentre un lugar alcanzable
		// en la direccion en la que se encuentre el robot orientado
		// for (Street s : cityMap) {
		// if (s.comeOutFrom(currentPlace, currentDirection)) {
		// success = true;
		// currentPlace = s.nextPlace(currentPlace);
		// return;
		// }
		Street s = cityMap.lookForStreet(currentPlace, currentDirection);
		if (s != null) {
			if (s.isOpen())
				success = true;
			else
				System.out.println(Message.CLOSED_STREET);
		}

		if (success && fuel >= 5) { // si hay calle, y tiene suficiente fuel
									// para moverse, se mueve
			addFuel(-5);
			currentPlace = s.nextPlace(currentPlace);
			System.out.println(Message.MOVING.replace("<dir>",
					currentDirection.toString()));
			System.out.println(currentPlace.toString());
			System.out.println(statusInfo());
			System.out.println(Message.LOOKING.replace("<dir>",
					currentDirection.toString()));
		} else if (success && fuel < 5)
			System.out.println(Message.NOT_ENOUGH_FUEL);
		else if (s==null)
			System.out.println(Message.NO_STREET.replace("<dir>",
					currentDirection.toString()));

	}

	/**
	 * Empieza el motor del robot
	 */
	public void startEngine() {
		Scanner sc = new Scanner(System.in);
		Interpreter interpreter = new Interpreter();
		Instruction instruction = new Instruction();

		System.out.println(currentPlace.toString());
		System.out.println(statusInfo());
		System.out.println(Message.LOOKING.replace("<dir>",
				currentDirection.toString()));

		// Bucle principal : mientras que la instruccion no sea Quit, o se
		// llegue a una nave espacial, solicita una nueva instruccion
		// al usuario y la procesa.
		while (instruction.getAction() != Action.QUIT
				&& !currentPlace.isSpaceship() && fuel > 0) {

			System.out.print(Message.PROMPT);
			String line = sc.nextLine();
			instruction = interpreter.generateInstruction(line);

			if (instruction.isValid()) {
				switch (instruction.getAction()) {
				case HELP:
					System.out.println(interpreter.interpreterHelp());
					break;
				case TURN:

					if (instruction.getRotation() == Rotation.LEFT)
						currentDirection = currentDirection.rotateLeft();
					else if (instruction.getRotation() == Rotation.RIGHT)
						currentDirection = currentDirection.rotateRight();

					addFuel(-1);
					System.out.println(statusInfo());
					System.out.println(Message.LOOKING.replace("<dir>",
							currentDirection.toString()));

					break;
				case MOVE:
					move();
					break;
				case PICK:
					pick(instruction, currentPlace);
					break;
				case OPERATE:
					operate(instruction, currentPlace);
					break;
				case SCAN:
					scan(instruction);
					break;
				case QUIT:
					System.out.println(Message.COM_PROBLEMS);
					break;
				default:
					break;
				}
			} else
				System.out.println(Message.NOT_UNDERSTAND);

		}
		if (fuel <= 0)
			System.out.println(Message.NO_FUEL);

		if (currentPlace.isSpaceship())
			System.out.println(Message.AT_SPACESHIP);
		sc.close();
	}

	/**
	 * Adds an amount of fuel to the robot (it can be negative)
	 * 
	 * @param fuel
	 *            - the amount of fuel
	 */
	public void addFuel(int fuel) {
		this.fuel += fuel;

		if (this.fuel < 0)
			this.fuel = 0;
	}

	/**
	 * Increases the amount of recycled material of the robot
	 * 
	 * @param weight
	 *            - Amount of recycled material
	 */
	public void addRecycledMaterial(int weight) {
		this.recycledMaterial += weight;
	}

	/**
	 * Returns the current fuel level of the robot. This method is mandatory FOR
	 * TESTING PURPOSES
	 */
	public int getFuel() {
		return this.fuel;
	}

	/**
	 * Returns the current weight of recycled material that the robot carries.
	 * This method is mandatory FOR TESTING PURPOSES
	 */
	public int getRecycledMaterial() {
		return this.recycledMaterial;
	}

	/**
	 * Returns the street opposite the robot
	 * 
	 * @return - The street which the robot is facing, or null, if there is not
	 *         any street in this direction
	 */
	public Street getHeadingStreet() {
		return cityMap.lookForStreet(currentPlace, currentDirection);
	}

	/**
	 * 
	 * @return a string containing relevant info to WALL�E's levels of power and
	 *         recycled material
	 */
	public java.lang.String statusInfo() {
		return "   * My power is " + this.fuel + LINE_SEPARATOR
				+ "   * My recycled material is: " + this.recycledMaterial;
	}

	/**
	 * Executes a PICK instruction
	 * 
	 * @param i
	 *            - Contains the info
	 */
	private void pick(Instruction i, Place curr) {
		Item tmp = curr.pickItem(i.getId());
		if (tmp == null)
			System.out.println(Message.NO_ITEM_IN_PLACE.replace("<id>",
					i.getId()));
		else {
			if (items.addItem(tmp))
				System.out.println(Message.ITEM_ADDED.replace("<id>",
						tmp.getId()));
			else
				System.out.println(Message.ITEM_REPEATED.replace("<id>",
						tmp.getId()));
		}
	}

	/**
	 * Executes an OPERATE instruction
	 * 
	 * @param i
	 *            - Contains the info
	 * @param curr
	 *            - The current place, needed in case item is a CodeCard
	 */
	private void operate(Instruction i, Place curr) {
		Item tmp = items.getItem(i.getId());
		if (tmp == null)
			System.out
					.println(Message.ITEM_PROBLEMS.replace("<id>", i.getId()));
		else {
			if (!tmp.use(this, curr))
				System.out.println(Message.ITEM_PROBLEMS.replace("<id>",
						tmp.getId()));
			else if (!tmp.canBeUsed()) {
				System.out.println(Message.NO_MORE_ITEM.replace("<id>",
						tmp.getId()));
				items.pickItem(tmp.getId());
			}
		}
	}

	/**
	 * Executes a SCAN instruction, let it be a single item (showing its
	 * description), or the list of all the items carried by the robot
	 * 
	 * @param i
	 *            - Contains the info
	 */
	private void scan(Instruction i) {
		if (i.getId() == null) {
			if (items.numberOfItems() == 0)
				System.out.println(Message.EMPTY_INVENTORY);
			else {
				System.out.println(Message.SHOWING_ITEMS);
				System.out.println(items.toString());
			}
		} else {
			Item tmp = items.getItem(i.getId());
			if (tmp == null)
				System.out.println(Message.NO_ITEM);
			else
				System.out.println("WALL·E says: " + tmp.toString());
		}
	}

}
